#include <algorithm>
#include <chrono>
#include <gsl/pointers>
#include <iomanip>
#include <iostream>
#include <optional>
#include <ostream>
#include <ranges>
#include <unordered_map>
#include <vector>

using CustomerId = int;

CustomerId get_current_customer_id() { return 42; }

struct Merchant {
  int id;
};

using namespace std::chrono;
using namespace std::literals::chrono_literals;

struct Item {
  std::string name;
  std::optional<std::string> photo_url;
  std::string description;
  std::optional<float> price;
  time_point<system_clock> date_added{};
  bool featured{};
};

std::ostream &operator<<(std::ostream &os, const Item *item) {
  // functor
  auto stringify_optional = [](const auto &optional) {
    using optional_value_type =
        typename std::remove_cvref_t<decltype(optional)>::value_type;
    if constexpr (std::is_same_v<optional_value_type, std::string>) {
      return optional ? *optional : "missing";
    } else {
      return optional ? std::to_string(*optional) : "missing";
    }
  };

  auto item_added = system_clock::to_time_t(item->date_added);

  os << "name: " << item->name
     << ", photo_url: " << stringify_optional(item->photo_url)
     << ", price: " << std::setprecision(2) << stringify_optional(item->price)
     << ", date_added: " << std::put_time(std::localtime(&time_added), "%c %2")
     << ", featured: " << item->featured;

  return os;
}

enum class Category {
  Food,
  Antiques,
  Books,
  Music,
  Photography,
  Handicraft,
  Artist,
};

struct Store {
  gsl::not_null<const Merchant *> owner;
  std::vector<Item> items;
  std::vector<Category> categories;
};

using Stores = std::vector<gsl::not_null<const Store*>>;

// methods
Stores get_favorite_stores_for(const CustomerId& cid) {
  static const auto merchants = std::vector<Merchant>{{17}, {29}};// two struct

  static const auto sts = std::vector<Store> {
    {.owner = &merchants[0],
     
      .items = {
       {
         .name = "honey",
         .photo_url = {},
         .description = "straight outta compton's apiary",
         .price = 9.99f,
         .date_added = system_clock::now(),
         .featured = false
       },
       {
         .name = "Oscypek",
         .photo_url = {},
         .description = "Tasty smoked cheese from the tetra mountains",
         .price = 1.23f,
         .date_added = system_clock::now() - 1h,
         .featured = true
       },
     },

     .categories = {{Category::Food}},

     {.owner = &merchants[1],

       .ietms = {
         {
          .name = "Handmade painted ceramic bowls",
          .photo_url = "http://example.com/beautiful_bowl.png",
          .description = "Hand-crafted and hand-decorated bowls made of fired clay",
          .price = {},
          .date_added = system_clock::now() - 12min,
          .featured = true
         }
       },

       .categories = {Category::Artist, Category::Handicraft}
     }
    }
  };

  static auto favorite_stores_by_customer = 
    std::unordered_map<CustomerId, Stores>{{42, {&sts[0], &sts[1]}}};

  return favorite_stores_by_customer[cid];
}

using Items = std::vector<gsl::not_null<const Item*>>;

Items get_featured_items_for_store(const Store& store) {
  auto featured = Items{};

  const auto &items = store.items;

  for (const auto &itm: items) {
    if (itm.featured) {
      featured.emplace_back(&itm);
    }
  }

  return featured;
}

Item get_all_featured_items(const Stores& sts) {
  auto all_featured = Items{};
  for (const auto &store : sts) {
    const auto featured_in_store = get_featured_items_for_store(*store);

    all_featured.reserve(all_featured.size() + featured_in_store.size());

    std::copy(std::begin(featured_in_store), std::end(featured_in_store), 
        std::back_inserter(all_featured)
        );
  }

  return all_featured;
}

void order_items_by_date_added(Items& itms) {
  auto date_comparator = []<typename T>(const T& lhs, const T& rhs) {
    return lhs->date_added > rhs->date_added;
  };

  std::sort(std::begin(itms), std::end(itms), date_comparator);
}

void render_item_gallery([[maybe_unused]] const Items& itms) {
  std::copy(
      std::begin(itms), std::end(itms),
      std::ostream_iterator<gsl::not_null<const Item*>>(std::cout, "\n")
      );
}

int main() {
  auto fav_stores = get_favorite_stores_for(get_current_customer_id());

  auto selected_items = get_all_featured_items(fav_stores);

  order_items_by_date_added(selected_items);

  render_item_gallery(selected_items);

  return 0;
}
